import React from 'react'
import {
  ADVANCED_SEARCH_TYPE,
  FETCH_CONFIG,
  COOKIE_FRONTEND,
  unLoggedAllowedPageList,
  history
} from './util/helper.js'

import { parseISO } from 'date-fns'
import i18n from './util/i18n.js'
import * as Cookies from 'js-cookie'
import {
  APP_LIST,
  ABOUT_USER,
  CONFIG,
  CONTENT,
  CONTENT_TYPE_LIST,
  FOLDER,
  FOLDER_READ,
  newFlashMessage,
  NOTIFICATION,
  NOTIFICATION_LIST,
  PUBLICATION_THREAD,
  SEARCHED_STRING,
  setRedirectLogin,
  setUserDisconnected,
  USAGE_CONDITIONS,
  USER,
  USER_CALL,
  USER_CONFIGURATION,
  USER_CONNECTED,
  USER_EMAIL,
  USER_KNOWN_MEMBER_LIST,
  USER_LANG,
  USER_LOGIN,
  USER_LOGOUT,
  USER_PASSWORD,
  USER_PUBLIC_NAME,
  USER_REQUEST_PASSWORD,
  USER_USERNAME,
  USER_WORKSPACE_EMAIL_NOTIFICATION_TYPE,
  USER_WORKSPACE_LIST,
  WORKSPACE,
  WORKSPACE_AGENDA_URL,
  WORKSPACE_CONTENT_ARCHIVED,
  WORKSPACE_CONTENT_DELETED,
  WORKSPACE_CONTENT_MOVE,
  WORKSPACE_CONTENT_PATH,
  WORKSPACE_CONTENT_SHARE_FOLDER,
  WORKSPACE_DETAIL,
  WORKSPACE_LIST,
  WORKSPACE_MEMBER_ADD,
  WORKSPACE_MEMBER_REMOVE,
  WORKSPACE_MEMBER_UPDATE,
  WORKSPACE_PUBLICATION_LIST,
  WORKSPACE_READ_STATUS,
  ACCESSIBLE_WORKSPACE_LIST,
  WORKSPACE_SUBSCRIPTION,
  WORKSPACE_SUBSCRIPTION_LIST,
  CUSTOM_PROPERTIES_UI_SCHEMA,
  CUSTOM_PROPERTIES_SCHEMA,
  USER_PUBLIC_PROFILE,
  FAVORITE_LIST,
  FAVORITE,
  UNREAD_NOTIFICATION_COUNT,
  ROLE_WORKSPACE_LIST
} from './action-creator.sync.js'
import {
  CONTENT_NAMESPACE,
  ErrorFlashMessageTemplateHtml,
  NUMBER_RESULTS_BY_PAGE,
  PAGE,
  TLM_CORE_EVENT_TYPE,
  TLM_ENTITY_TYPE,
  CONTENT_TYPE,
  CUSTOM_EVENT,
  updateTLMUser,
  uploadFile
} from 'tracim_frontend_lib'

/*
 * fetchWrapper(obj)
 *
 * Params:
 *   An Object with the following attributes :
 *     url - string - url of the end point to call
 *     param - object - param to send with fetch call (eg. header)
 *       param.method - string - REQUIRED - method of the http call
 *     actionName - string - name of the action to dispatch with 'PENDING' and 'SUCCESS' respectively before and after the http request
 *     dispatch - func - redux dispatcher function
 *
 * Returns:
 *   An object Response generated by whatwg-fetch with a new property 'json' containing the data received or informations in case of failure
 *
 * This function create a http async request using whatwg-fetch while dispatching a PENDING and a SUCCESS redux action.
 * It also adds, to the Response of the fetch request, the json value so that the redux action have access to the status and the data
 */
// Côme - 2018/08/02 - fetchWrapper should come from tracim_lib so that all apps uses the same
// 08/09/2018 - maybe not since this fetchWrapper also dispatch redux actions whether it succeed or failed
const fetchWrapper = async ({ url, param, actionName, dispatch }) => {
  dispatch({ type: `${param.method}/${actionName}/PENDING` })

  try {
    const fetchResult = await fetch(url, param)
    fetchResult.json = await (async () => { // await for the .json()
      const status = fetchResult.status
      if (status === 204) return ''
      if (status >= 200 && status <= 299) return fetchResult.json()
      if (status >= 300 && status <= 399) return fetchResult.json()
      if (status === 401) {
        // FIME - GB - 2019-02-08 - Find a better way of handling the list of unLoggedAllowedPageList
        // https://github.com/tracim/tracim/issues/2144
        if (!unLoggedAllowedPageList.some(url => document.location.pathname.startsWith(url))) {
          dispatch(setRedirectLogin(document.location.pathname + document.location.search))
          dispatch(setUserDisconnected())
          history.push(`${PAGE.LOGIN}${Cookies.get(COOKIE_FRONTEND.LAST_CONNECTION) ? '?dc=1' : ''}`)
          Cookies.remove(COOKIE_FRONTEND.LAST_CONNECTION)
        }
        return ''
      }
      if (status >= 400 && status <= 499) return fetchResult.json()
      if (status >= 500 && status <= 599) {
        const errorData = await fetchResult.json()
        let errorDetails = ''
        if (errorData && errorData.code && errorData.message) {
          errorDetails = `${errorData.code}: ${errorData.message}`
        }
        dispatch(newFlashMessage(
          <ErrorFlashMessageTemplateHtml errorMsg={errorDetails} />, 'danger', 8000
        ))
        return
      }
      dispatch(newFlashMessage(
        <ErrorFlashMessageTemplateHtml errorMsg={`Unknown http status ${fetchResult.status}`} />, 'danger', 300000
      ))
    })()

    const status = fetchResult.status
    if (status >= 200 && status <= 399) {
      dispatch({ type: `${param.method}/${actionName}/SUCCESS`, data: fetchResult.json })
    } else {
      dispatch({ type: `${param.method}/${actionName}/FAILED`, data: fetchResult.json })
    }

    return fetchResult
  } catch (e) {
    if (e instanceof TypeError) {
      dispatch(newFlashMessage(i18n.t('Server unreachable'), 'danger'))
      console.error(e)
    }
    return { status: 'failedToFetch' } // Côme - 2018/10/08 - this status is unused, the point is only to return an object with a status attribute
  }
}

export const postUserLogin = (credentials, rememberMe) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/auth/login`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'POST',
      body: JSON.stringify({
        ...credentials
        // remember_me: rememberMe
      })
    },
    actionName: USER_LOGIN,
    dispatch
  })
}

export const postUserRegister = (newUser) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/register`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'POST',
      body: JSON.stringify(newUser)
    },
    actionName: USER_LOGIN,
    dispatch
  })
}

export const postForgotPassword = login => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/auth/password/reset/request`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'POST',
      body: JSON.stringify({
        ...login
      })
    },
    actionName: USER_REQUEST_PASSWORD,
    dispatch
  })
}

export const postResetPassword = (newPassword, newPassword2, email, token) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/auth/password/reset/modify`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'POST',
      body: JSON.stringify({
        email: email,
        new_password: newPassword,
        new_password2: newPassword2,
        reset_password_token: token
      })
    },
    actionName: USER_REQUEST_PASSWORD,
    dispatch
  })
}

export const postUserLogout = () => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/auth/logout`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'POST'
    },
    actionName: USER_LOGOUT,
    dispatch
  })
}

export const getUser = userId => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: USER,
    dispatch
  })
}

export const getAboutUser = userId => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/about`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: ABOUT_USER,
    dispatch
  })
}

export const getUserConfiguration = userId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/config`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'GET'
    },
    actionName: USER_CONFIGURATION,
    dispatch
  })
}

// TODO - CH - 2023-11-02 - this function should be renamed according to:
// https://github.com/tracim/tracim/issues/6252
export const getUserRoleWorkspaceList = (userId) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/workspaces/all/settings`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: USER_WORKSPACE_LIST,
    dispatch
  })
}

export const getUserIsConnected = () => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/auth/whoami`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: USER_CONNECTED,
    dispatch
  })
}

export const getMyselfAllKnownMember = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/known_members`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: USER_KNOWN_MEMBER_LIST,
    dispatch
  })
}

export const putMyselfName = (user, newName) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        public_name: newName,
        timezone: user.timezone,
        lang: user.lang
      })
    },
    actionName: USER_PUBLIC_NAME,
    dispatch
  })
}

export const putUserPublicName = (user, newName) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${user.userId}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        public_name: newName,
        timezone: user.timezone,
        lang: user.lang
      })
    },
    actionName: USER_PUBLIC_NAME,
    dispatch
  })
}

export const putUserUsername = (user, newUsername, checkPassword) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${user.userId}/username`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        username: newUsername,
        loggedin_user_password: checkPassword
      })
    },
    actionName: USER_USERNAME,
    dispatch
  })
}

export const putMyselfEmail = (newEmailWithoutTrim, checkPassword) => dispatch => {
  const newEmail = newEmailWithoutTrim.trim()

  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/email`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        email: newEmail,
        loggedin_user_password: checkPassword
      })
    },
    actionName: USER_EMAIL,
    dispatch
  })
}

export const putUserEmail = (user, newEmailWithoutTrim, checkPassword) => dispatch => {
  const newEmail = newEmailWithoutTrim.trim()

  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${user.userId}/email`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        email: newEmail,
        loggedin_user_password: checkPassword
      })
    },
    actionName: USER_EMAIL,
    dispatch
  })
}

export const putMyselfPassword = (oldPassword, newPassword, newPassword2) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/password`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        loggedin_user_password: oldPassword,
        new_password: newPassword,
        new_password2: newPassword2
      })
    },
    actionName: USER_PASSWORD,
    dispatch
  })
}

export const putUserPassword = (userId, oldPassword, newPassword, newPassword2) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/password`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        loggedin_user_password: oldPassword,
        new_password: newPassword,
        new_password2: newPassword2
      })
    },
    actionName: USER_PASSWORD,
    dispatch
  })
}

export const putUserLang = (user, newLang) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${user.userId}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        public_name: user.publicName,
        timezone: user.timezone,
        lang: newLang
      })
    },
    actionName: USER_LANG,
    dispatch
  })
}

export const putMyselfWorkspaceEmailNotificationType = (workspaceId, emailNotificationType) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/workspaces/${workspaceId}/email_notification_type`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        email_notification_type: emailNotificationType
      })
    },
    actionName: USER_WORKSPACE_EMAIL_NOTIFICATION_TYPE,
    dispatch
  })
}

export const putUserWorkspaceEmailNotificationType = (user, workspaceId, emailNotificationType) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${user.userId}/workspaces/${workspaceId}/email_notification_type`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        email_notification_type: emailNotificationType
      })
    },
    actionName: USER_WORKSPACE_EMAIL_NOTIFICATION_TYPE,
    dispatch
  })
}

// TODO - CH - 2023-11-02 - this function should be renamed according to:
// https://github.com/tracim/tracim/issues/6252
export const getMyselfUserRoleWorkspaceList = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/workspaces/all/settings`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: ROLE_WORKSPACE_LIST,
    dispatch
  })
}

export const getWorkspaceList = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_LIST,
    dispatch
  })
}

export const getWorkspaceDetail = (workspaceId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_DETAIL,
    dispatch
  })
}

export const getContent = (contentId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/contents/${contentId}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: CONTENT,
    dispatch
  })
}

export const getFolderContentList = (workspaceId, folderIdList) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents?parent_ids=${folderIdList.join(',')}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: FOLDER,
    dispatch
  })
}

export const getSubFolderShareContentList = (workspaceId, folderIdList) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents?namespaces_filter=upload&parent_ids=${folderIdList.join(',')}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE,
    dispatch
  })
}

export const getShareFolderContentList = (workspaceId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents?namespaces_filter=upload`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_CONTENT_SHARE_FOLDER,
    dispatch
  })
}

export const getContentPathList = (workspaceId, contentId, folderIdList) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents?complete_path_to_id=${contentId}&parent_ids=${folderIdList.join(',')}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_CONTENT_PATH,
    dispatch
  })
}

export const getMyselfWorkspaceReadStatusList = workspaceId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/workspaces/${workspaceId}/contents/read_status`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_READ_STATUS,
    dispatch
  })
}

export const postWorkspaceMember = (workspaceId, newMember) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/members`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'POST',
      body: JSON.stringify({
        user_id: newMember.id || null,
        user_email: newMember.email || null,
        user_username: newMember.username || null,
        role: newMember.role
      })
    },
    actionName: WORKSPACE_MEMBER_ADD,
    dispatch
  })
}

export const deleteWorkspaceMember = (workspaceId, memberId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/members/${memberId}`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'DELETE'
    },
    actionName: WORKSPACE_MEMBER_REMOVE,
    dispatch
  })
}

export const updateWorkspaceMember = (workspaceId, memberId, newRole) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/members/${memberId}`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'PUT',
      body: JSON.stringify({
        role: newRole
      })
    },
    actionName: WORKSPACE_MEMBER_UPDATE,
    dispatch
  })
}

export const getFolderContent = (workspaceId, folderId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents?parent_id=${folderId}`,
    param: {
      credentials: 'include',
      headers: { ...FETCH_CONFIG.headers },
      method: 'GET'
    },
    actionName: `${WORKSPACE}/${FOLDER}`,
    dispatch
  })
}

export const getConfig = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/system/config`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: CONFIG,
    dispatch
  })
}

export const getAppList = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/system/applications`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: APP_LIST,
    dispatch
  })
}

export const getContentTypeList = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/system/content_types`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: CONTENT_TYPE_LIST,
    dispatch
  })
}

export const putWorkspaceContentArchived = (workspaceId, contentId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents/${contentId}/archived`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT'
    },
    actionName: WORKSPACE_CONTENT_ARCHIVED,
    dispatch
  })
}

export const putWorkspaceContentDeleted = (workspaceId, contentId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents/${contentId}/trashed`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT'
    },
    actionName: WORKSPACE_CONTENT_DELETED,
    dispatch
  })
}

export const putFolderRead = (userId, workspaceId, contentId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/workspaces/${workspaceId}/contents/${contentId}/read`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT'
    },
    actionName: FOLDER_READ,
    dispatch
  })
}

export const getLoggedUserCalendar = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/me/agenda`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_AGENDA_URL,
    dispatch
  })
}

export const getUserCalendar = userId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/agenda`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_AGENDA_URL,
    dispatch
  })
}

export const getSimpleSearchResult = (contentTypes, searchString, pageNumber, pageSize, showArchived, showDeleted, showActive) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/search/content?show_archived=${showArchived ? 1 : 0}&content_types=${contentTypes}&show_deleted=${showDeleted ? 1 : 0}&show_active=${showActive ? 1 : 0}&search_string=${encodeURIComponent(searchString)}&page_nb=${pageNumber}&size=${pageSize}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: SEARCHED_STRING,
    dispatch
  })
}

export const putContentItemMove = (source, destination) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${source.workspaceId}/contents/${source.contentId}/move`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        new_workspace_id: destination.workspaceId || 0,
        new_parent_id: destination.contentId || 0
      })
    },
    actionName: WORKSPACE_CONTENT_MOVE,
    dispatch
  })
}

export const getFileInfos = (token) =>
  fetch(`${FETCH_CONFIG.apiUrl}/public/guest-download/${token}`, {
    credentials: 'include',
    headers: {
      ...FETCH_CONFIG.headers
    },
    method: 'GET'
  })

export const postDownloadFile = (token, guestPassword) =>
  fetch(`${FETCH_CONFIG.apiUrl}/public/guest-download/${token}/check`, {
    credentials: 'include',
    headers: {
      ...FETCH_CONFIG.headers
    },
    method: 'POST',
    body: JSON.stringify({
      password: guestPassword
    })
  })

export const getGuestUploadInfo = token => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/public/guest-upload/${token}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: 'GuestUpload',
    dispatch
  })
}

const defaultExcludedEventTypesParam = '&exclude_event_types=' + global.GLOBAL_excludedNotifications.join(',')

const activityExcludedEventTypesParam = '&exclude_event_types=' + global.GLOBAL_excludedNotifications.filter(
  ev => {
    const [entityType, eventType] = ev.split('.')
    return (entityType !== TLM_ENTITY_TYPE.CONTENT || eventType !== TLM_CORE_EVENT_TYPE.MODIFIED)
  }
).join(',')

export const getNotificationList = (
  userId,
  {
    excludeAuthorId = null,
    notificationsPerPage = NUMBER_RESULTS_BY_PAGE,
    nextPageToken = null,
    workspaceId = null,
    includeNotSent = false,
    recentActivitiesEvents = false,
    relatedContentId = null
  }) => async dispatch => {
  const queryParameterList = [
    recentActivitiesEvents
      ? activityExcludedEventTypesParam
      : defaultExcludedEventTypesParam
  ]
  if (excludeAuthorId) queryParameterList.push(`exclude_author_ids=${excludeAuthorId}`)
  if (notificationsPerPage > 0) queryParameterList.push(`count=${notificationsPerPage}`)
  if (nextPageToken) queryParameterList.push(`page_token=${nextPageToken}`)
  if (workspaceId) queryParameterList.push(`workspace_ids=${workspaceId}`)
  if (includeNotSent) queryParameterList.push('include_not_sent=1')
  if (relatedContentId) queryParameterList.push(`related_to_content_ids=${relatedContentId}`)
  const fetchGetNotificationWall = await fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/messages?${queryParameterList.join('&')}`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'GET'
    },
    actionName: NOTIFICATION_LIST,
    dispatch
  })

  if (fetchGetNotificationWall.status === 200) {
    fetchGetNotificationWall.json.items = fetchGetNotificationWall.json.items.map(notification => ({
      ...notification,
      fields: {
        ...notification.fields,
        author: updateTLMUser(notification.fields.author, true),
        user: updateTLMUser(notification.fields.user)
      }
    }))
  }
  return fetchGetNotificationWall
}

/**
 * Put a list of notifications as read
 * @param {String} userId
 * @param {int[]} notificationIdList
 * @returns
 */
export const putNotificationListAsRead = (userId, notificationIdList) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/messages/read` +
      `?event_ids=${notificationIdList.join(',')}`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'PUT'
    },
    actionName: NOTIFICATION_LIST,
    dispatch
  })
}

/**
 * Put a list of space notifications as read
 * @param {String} userId
 * @param {int[]} spaceIdList
 * @returns
 */
export const putSpaceListAsRead = (userId, spaceIdList) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/messages/read?space_ids=${spaceIdList.join(',')}`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'PUT'
    },
    actionName: NOTIFICATION_LIST,
    dispatch
  })
}

export const putContentNotificationAsRead = (userId, contentId, parentId = null) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/messages/read?content_ids=${contentId}${parentId ? `&parent_ids=${parentId}` : ''}`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'PUT'
    },
    actionName: `${NOTIFICATION}/${CONTENT}`,
    dispatch
  })
}

export const putAllNotificationAsRead = (userId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/messages/read`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'PUT'
    },
    actionName: NOTIFICATION_LIST,
    dispatch
  })
}

export const getUserMessagesSummary = (userId, includeEventTypeList = []) => dispatch => {
  const url = `${FETCH_CONFIG.apiUrl}/users/${userId}/messages/summary?exclude_author_ids=${userId}${defaultExcludedEventTypesParam}`
  const includeEventTypeListParam = includeEventTypeList.length > 0 ? `&include_event_types=${includeEventTypeList.join(',')}` : ''
  const fetchGetMessages = fetchWrapper({
    url: `${url}${includeEventTypeListParam}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: UNREAD_NOTIFICATION_COUNT,
    dispatch
  })

  if (fetchGetMessages.status === 200) {
    fetchGetMessages.json.items = fetchGetMessages.json.items.map(message => ({
      ...message,
      fields: {
        ...message.fields,
        author: updateTLMUser(message.fields.author, true),
        user: updateTLMUser(message.fields.user)
      }
    }))
  }
  return fetchGetMessages
}

export const getAccessibleWorkspaces = userId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/accessible_workspaces`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: ACCESSIBLE_WORKSPACE_LIST,
    dispatch
  })
}

export const getWorkspaceSubscriptions = userId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/workspace_subscriptions`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_SUBSCRIPTION_LIST,
    dispatch
  })
}

export const getSubscriptions = workspaceId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/subscriptions`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_SUBSCRIPTION,
    dispatch
  })
}

export const postUserWorkspace = (workspaceId, userId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/workspaces`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'POST',
      body: JSON.stringify({
        workspace_id: workspaceId
      })
    },
    actionName: WORKSPACE_LIST,
    dispatch
  })
}

export const putUserWorkspaceSubscription = (workspaceId, userId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/workspace_subscriptions`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({
        workspace_id: workspaceId
      })
    },
    actionName: WORKSPACE_SUBSCRIPTION_LIST,
    dispatch
  })
}

export const getHTMLPreview = (workspaceId, contentType, contentId, label) => {
  // RJ - NOTE - 17-11-2020 - this uses fetch instead of fetchWrapper due to the
  // specific error handling
  const filename = encodeURIComponent(label.replace(/\//g, '_'))
  return fetch(`${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/${contentType}s/${contentId}/preview/html/${filename}.html`, {
    credentials: 'include',
    headers: FETCH_CONFIG.headers,
    method: 'GET'
  })
}

export const getCustomPropertiesSchema = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/system/user-custom-properties-schema`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'GET'
    },
    actionName: CUSTOM_PROPERTIES_SCHEMA,
    dispatch
  })
}

export const getCustomPropertiesUiSchema = () => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/system/user-custom-properties-ui-schema`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'GET'
    },
    actionName: CUSTOM_PROPERTIES_UI_SCHEMA,
    dispatch
  })
}

export const getUserCustomPropertiesDataSchema = userId => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/custom-properties`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'GET'
    },
    actionName: USER_PUBLIC_PROFILE,
    dispatch
  })
}

export const putUserCustomPropertiesDataSchema = (userId, formData) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/custom-properties`,
    param: {
      credentials: 'include',
      headers: FETCH_CONFIG.headers,
      method: 'PUT',
      body: JSON.stringify({
        parameters: formData
      })
    },
    actionName: USER_PUBLIC_PROFILE,
    dispatch
  })
}

const getUTCMidnight = (dateString) => parseISO(`${dateString}T00:00:00Z`)

const getDateRangeParameters = (range, rangeParameterPrefix) => {
  const rangeParameterList = []
  if (range.from) {
    const fromDate = getUTCMidnight(range.from)
    // HACK - S.G - 2021-03-09 - Remove milliseconds as the backend
    // does not handle them, but keep the UTC zone as it is mandatory.
    const fromDateString = fromDate.toISOString().split('.')[0] + 'Z'
    rangeParameterList.push(`${rangeParameterPrefix}_from=${fromDateString}`)
  }
  if (range.to) {
    const toDate = getUTCMidnight(range.to)
    toDate.setDate(toDate.getDate() + 1)
    // HACK - S.G - 2021-03-09 - Remove milliseconds as the backend
    // does not handle them, but keep the UTC zone as it is mandatory.
    const toDateString = toDate.toISOString().split('.')[0] + 'Z'
    rangeParameterList.push(`${rangeParameterPrefix}_to=${toDateString}`)
  }
  return rangeParameterList
}

const encodeArrayAsURIComponent = (arrayOrItem, separator = ',') => {
  if (!(arrayOrItem instanceof Array)) return encodeURIComponent(arrayOrItem)
  return arrayOrItem.map(item => encodeURIComponent(item)).join(',')
}

export const getAdvancedSearchResult = (
  searchString,
  contentTypes,
  pageNumber,
  pageSize,
  showArchived,
  showDeleted,
  showActive,
  searchType,
  searchFieldList,
  createdRange,
  modifiedRange,
  newestAuthoredContentRange,
  searchFacets
) => dispatch => {
  let queryParameterList = []
  if (searchString) queryParameterList.push(`search_string=${encodeURIComponent(searchString)}`)
  else queryParameterList.push('search_string=*')
  if (pageNumber) queryParameterList.push(`page_nb=${pageNumber}`)
  if (Number.isInteger(pageSize)) queryParameterList.push(`size=${pageSize}`)
  if (showActive) queryParameterList.push(`show_active=${showActive ? 1 : 0}`)
  if (showDeleted) queryParameterList.push(`show_deleted=${showDeleted ? 1 : 0}`)
  if (searchFieldList) queryParameterList.push(`search_fields=${searchFieldList}`)
  if (searchType === ADVANCED_SEARCH_TYPE.CONTENT) {
    if (contentTypes) queryParameterList.push(`content_types=${contentTypes}`)
    if (showArchived) queryParameterList.push(`show_archived=${showArchived ? 1 : 0}`)
    if (createdRange) queryParameterList = queryParameterList.concat(getDateRangeParameters(createdRange, 'created'))
    if (modifiedRange) queryParameterList = queryParameterList.concat(getDateRangeParameters(modifiedRange, 'modified'))
    if (searchFacets) {
      if (searchFacets.workspace_names && searchFacets.workspace_names.length > 0) queryParameterList.push(`workspace_names=${encodeArrayAsURIComponent(searchFacets.workspace_names)}`)
      if (searchFacets.statuses && searchFacets.statuses.length > 0) queryParameterList.push(`statuses=${encodeArrayAsURIComponent(searchFacets.statuses)}`)
      if (searchFacets.content_types && searchFacets.content_types.length > 0) queryParameterList.push(`content_types=${encodeArrayAsURIComponent(searchFacets.content_types)}`)
      if (searchFacets.file_extensions && searchFacets.file_extensions.length > 0) queryParameterList.push(`file_extensions=${encodeArrayAsURIComponent(searchFacets.file_extensions)}`)
      if (searchFacets.author__public_names && searchFacets.author__public_names.length > 0) queryParameterList.push(`author__public_names=${encodeArrayAsURIComponent(searchFacets.author__public_names)}`)
      if (searchFacets.tags && searchFacets.tags.length > 0) queryParameterList.push(`tags=${encodeArrayAsURIComponent(searchFacets.tags)}`)
    }
  }
  if (searchType === ADVANCED_SEARCH_TYPE.USER) {
    if (searchFacets && searchFacets.workspace_ids) queryParameterList.push(`workspace_ids=${searchFacets.workspace_ids}`)
    if (newestAuthoredContentRange) {
      queryParameterList = queryParameterList.concat(getDateRangeParameters(newestAuthoredContentRange, 'newest_authored_content_date'))
    }
  }
  if (searchType === ADVANCED_SEARCH_TYPE.SPACE) {
    if (searchFacets && searchFacets.members) queryParameterList.push(`member_ids=${searchFacets.members}`)
    if (searchFacets && searchFacets.owners) queryParameterList.push(`owner_ids=${searchFacets.owners}`)
  }

  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/advanced_search/${searchType}?${queryParameterList.join('&')}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: SEARCHED_STRING,
    dispatch
  })
}

export const getPublicationPage = (workspaceId, count = 0, pageToken = '') => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents?namespaces_filter=publication&parent_ids=0&count=${count}&page_token=${pageToken}&sort=modified:desc`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: WORKSPACE_PUBLICATION_LIST,
    dispatch
  })
}

export const postThreadPublication = (workspaceId, newContentName) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/contents`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'POST',
      body: JSON.stringify({
        parent_id: null,
        content_type: CONTENT_TYPE.THREAD,
        content_namespace: CONTENT_NAMESPACE.PUBLICATION,
        label: newContentName
      })
    },
    actionName: PUBLICATION_THREAD,
    dispatch
  })
}

export const postPublicationFile = (workspaceId, content, label) => async dispatch => {
  const errorMessageList = [
    { status: 400, code: 3002, message: i18n.t('A content with the same name already exists') },
    { status: 400, code: 6002, message: i18n.t('The file is larger than the maximum file size allowed') },
    { status: 400, code: 6003, message: i18n.t('Error, the space exceed its maximum size') },
    { status: 400, code: 6004, message: i18n.t('You have reached your storage limit, you cannot add new files') }
  ]
  const result = await uploadFile(
    content,
    `${FETCH_CONFIG.apiUrl}/workspaces/${workspaceId}/files`,
    {
      additionalFormData: {
        // FIXME - CH - 20210325 - the parent_id should be the same as the parent_id in postPublication()
        // see https://github.com/tracim/tracim/issues/1180 and https://github.com/tracim/tracim/issues/3937
        parent_id: 0,
        label: label,
        content_namespace: CONTENT_NAMESPACE.PUBLICATION
      },
      httpMethod: 'POST',
      progressEventHandler: () => {},
      errorMessageList: errorMessageList,
      defaultErrorMessage: i18n.t('Error while uploading file')
    }
  )
  return result
}

export const getFavoriteContentList = (userId) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/favorite-contents`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: FAVORITE_LIST,
    dispatch
  })
}

export const postContentToFavoriteList = (userId, contentId) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/favorite-contents`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'POST',
      body: { content_id: contentId }
    },
    actionName: FAVORITE,
    dispatch
  })
}

export const deleteContentFromFavoriteList = (userId, contentId) => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${userId}/favorite-contents/${contentId}`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'DELETE'
    },
    actionName: FAVORITE,
    dispatch
  })
}

export const getUsageConditions = () => async dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/system/usage_conditions`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'GET'
    },
    actionName: USAGE_CONDITIONS,
    dispatch
  })
}

export const logoutUser = (history) => async (dispatch) => {
  const fetchPostUserLogout = await dispatch(postUserLogout())
  if (fetchPostUserLogout.status === 204) {
    dispatch(setUserDisconnected())
    GLOBAL_dispatchEvent(CUSTOM_EVENT.USER_DISCONNECTED, {})
    history.push(PAGE.LOGIN)
    Cookies.remove(COOKIE_FRONTEND.SHOW_USERNAME_POPUP)
  } else {
    dispatch(newFlashMessage(i18n.t('Disconnection error', 'danger')))
  }
}

export const postCreateUserCall = (callerId, calleeId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${callerId}/outgoing_calls`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'POST',
      body: JSON.stringify({ callee_id: calleeId })
    },
    actionName: USER_CALL,
    dispatch
  })
}

export const putSetOutgoingUserCallState = (callerId, callId, userCallState) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${callerId}/outgoing_calls/${callId}/state`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({ state: userCallState })
    },
    actionName: USER_CALL,
    dispatch
  })
}

export const putSetIncomingUserCallState = (callerId, callId, userCallState) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${callerId}/incoming_calls/${callId}/state`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'PUT',
      body: JSON.stringify({ state: userCallState })
    },
    actionName: USER_CALL,
    dispatch
  })
}

export const postGetUserCall = (callerId, calleeId) => dispatch => {
  return fetchWrapper({
    url: `${FETCH_CONFIG.apiUrl}/users/${calleeId}/incoming_calls`,
    param: {
      credentials: 'include',
      headers: {
        ...FETCH_CONFIG.headers
      },
      method: 'POST',
      body: JSON.stringify({ callee_id: calleeId })
    },
    actionName: USER_CALL,
    dispatch
  })
}
